<?php
include_once (_CORE_PATH . "lib_prototype.php");
class str_class implements lib_prototype_class
{
    private $_isMBString = false; 
    
    public function __construct()
    {
        //эта строка устанавливает базовую локаль, гарантирует, что стандартные строковые функции не будут работать с UTF
        setlocale ( LC_CTYPE, 'C' );        
        
        if (extension_loaded('mbstring')) 
        {
            $this->_isMBString = true;
			mb_internal_encoding("UTF-8");
			mb_regex_encoding("UTF-8"); 
        }	       
    }
    
	public function IsUTF8($str) {
	    if ($this->_isMBString) mb_check_encoding($str);
	    
		if ( strlen($str) == 0 ) {
		    return TRUE;
		}
		// If even just the first character can be matched, when the /u
		// modifier is used, then it's valid UTF-8. If the UTF-8 is somehow
		// invalid, nothing at all will match, even if the string contains
		// some valid sequences
		return (preg_match('/^.{1}/us',$str,$ar) == 1);
	}    
    
	/**
	 * Более продвинутый аналог strip_tags() для корректного вырезания тагов из html кода.
	 * Функция strip_tags(), в зависимости от контекста, может работать не корректно.
	 * Возможности:
	 *   - корректно обрабатываются вхождения типа "a < b > c"
	 *   - корректно обрабатывается "грязный" html, когда в значениях атрибутов тагов могут встречаться символы < >
	 *   - корректно обрабатывается разбитый html
	 *   - вырезаются комментарии, скрипты, стили, PHP, Perl, ASP код, MS Word таги, CDATA
	 *   - автоматически форматируется текст, если он содержит html код
	 *   - защита от подделок типа: "<<fake>script>alert('hi')</</fake>script>"
	 * @param   string  $s
	 * @param   array   $allowable_tags     Массив тагов, которые не будут вырезаны
	 * @param   bool    $is_format_spaces   Форматировать пробелы и переносы строк?
	 *                                      Вид текста на выходе (plain) максимально приближеется виду текста в браузере на входе.
	 *                                      Другими словами, грамотно преобразует text/html в text/plain.
	 *                                      Текст форматируется только в том случае, если были вырезаны какие-либо таги.
	 * @param   array   $pair_tags   массив имён парных тагов, которые будут удалены вместе с содержимым
	 *                               см. значения по умолчанию
	 * @param   array   $para_tags   массив имён парных тагов, которые будут восприниматься как параграфы (если $is_format_spaces = true)
	 *                               см. значения по умолчанию
	 * @return  string 
	 */	
	public function StripTags(
	    /*string*/ $s,
	    array $allowable_tags = null,
	    /*boolean*/ $is_format_spaces = true,
	    array $pair_tags = array('script', 'style', 'map', 'iframe', 'frameset', 'object', 'applet', 'comment', 'button'),
	    array $para_tags = array('p', 'td', 'th', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'form', 'title', 'pre', 'textarea')
	)
	{
	    //return strip_tags($s);
	    static $_callback_type  = false;
	    static $_allowable_tags = array();
	    static $_para_tags      = array();
	    #регулярное выражение для атрибутов тагов
	    #корректно обрабатывает грязный и битый HTML в однобайтовой или UTF-8 кодировке!
	    static $re_attrs_fast_safe =  '(?![a-zA-Z\d])  #утверждение, которое следует сразу после тага
	                                   #правильные атрибуты
	                                   (?>
	                                       [^>"\']+
	                                     | (?<=[\=\x20\r\n\t]|\xc2\xa0) "[^"]*"
	                                     | (?<=[\=\x20\r\n\t]|\xc2\xa0) \'[^\']*\'
	                                   )*
	                                   #разбитые атрибуты
	                                   [^>]*';
	
	    if (is_array($s))
	    {
	        if ($_callback_type === 'strip_tags')
	        {
	            $tag = strtolower($s[1]);
	            if ($_allowable_tags &&
	                (array_key_exists($tag, $_allowable_tags) || array_key_exists('<' . trim(strtolower($s[0]), '< />') . '>', $_allowable_tags))
	                ) return $s[0];
	            if ($tag == 'br') return "\r\n";
	            if ($_para_tags && array_key_exists($tag, $_para_tags)) return "\r\n\r\n";
	            return '';
	        }
	        if ($_callback_type === 'strip_spaces')
	        {
	            if ($s[0]{0} === '<') return $s[0];
	            return ' ';
	        }
	        trigger_error('Unknown callback type "' . $_callback_type . '"!', E_USER_ERROR);
	    }
	
	    if (($pos = strpos($s, '<')) === false || strpos($s, '>', $pos) === false)  #оптимизация скорости
	    {
	        #таги не найдены
	        return $s;
	    }
	
	    $length = strlen($s);
	
	    #непарные таги (открывающие, закрывающие, !DOCTYPE, MS Word namespace)
	    $re_tags = '/<[\/\!]?+ ([a-zA-Z][a-zA-Z\d]*+ (?>\:[a-zA-Z][a-zA-Z\d]*)?)' . $re_attrs_fast_safe . '>/sxS';
	
	    $patterns = array(
	        '/<([\?\%]) .*? \\1>/sxS',     #встроенный PHP, Perl, ASP код
	        '/<\!\[CDATA\[ .*? \]\]>/sxS', #блоки CDATA
	        #'/<\!\[  [\x20\r\n\t]* [a-zA-Z] .*?  \]>/sxS',  #:DEPRECATED: MS Word таги типа <![if! vml]>...<![endif]>
	
	        '/<\!--.*?-->/sS', #комментарии
	
	        #MS Word таги типа "<![if! vml]>...<![endif]>",
	        #условное выполнение кода для IE типа "<!--[if expression]> HTML <![endif]-->"
	        #условное выполнение кода для IE типа "<![if expression]> HTML <![endif]>"
	        #см. http://www.tigir.com/comments.htm
	        '/<\! (?:--)?+
	              \[
	              (?> [^\]"\']+ | "[^"]*" | \'[^\']*\' )*
	              \]
	              (?:--)?+
	         >/sxS',
	    );
	    if ($pair_tags)
	    {
	        #парные таги вместе с содержимым:
	        foreach ($pair_tags as $k => $v) $pair_tags[$k] = preg_quote($v, '/');
	        $patterns[] = '/<((?i:' . implode('|', $pair_tags) . '))' . $re_attrs_fast_safe . '> .*? <\/(?i:\\1)' . $re_attrs_fast_safe . '>/sxS';
	    }
	    #d($patterns);
	
	    $i = 0; #защита от зацикливания
	    $max = 99;
	    while ($i < $max)
	    {
	        $s2 = preg_replace($patterns, '', $s);
	        if (preg_last_error() !== PREG_NO_ERROR)
	        {
	            $i = 999;
	            break;
	        }
	
	        if ($i == 0)
	        {
	            $is_html = ($s2 != $s || preg_match($re_tags, $s2));
	            if (preg_last_error() !== PREG_NO_ERROR)
	            {
	                $i = 999;
	                break;
	            }
	            if ($is_html)
	            {
	                if ($is_format_spaces)
	                {
	                    #В библиотеке PCRE для PHP \s - это любой пробельный символ, а именно класс символов [\x09\x0a\x0c\x0d\x20\xa0] или, по другому, [\t\n\f\r \xa0]
	                    #Если \s используется с модификатором /u, то \s трактуется как [\x09\x0a\x0c\x0d\x20]
	                    #Браузер не делает различия между пробельными символами,
	                    #друг за другом подряд идущие символы воспринимаются как один
	                    #$s2 = str_replace(array("\r", "\n", "\t"), ' ', $s2);
	                    #$s2 = strtr($s2, "\x09\x0a\x0c\x0d", '    ');
	                    $_callback_type = 'strip_spaces';
	                    $s2 = preg_replace_callback('/  [\x09\x0a\x0c\x0d]++
	                                                  | <((?i:pre|textarea))' . $re_attrs_fast_safe . '>
	                                                    .+?
	                                                    <\/(?i:\\1)' . $re_attrs_fast_safe . '>
	                                                 /sxS', array($this, __METHOD__), $s2);
	                    $_callback_type = false;
	                    if (preg_last_error() !== PREG_NO_ERROR)
	                    {
	                        $i = 999;
	                        break;
	                    }
	                }
	
	                #массив тагов, которые не будут вырезаны
	                if ($allowable_tags) $_allowable_tags = array_flip($allowable_tags);
	
	                #парные таги, которые будут восприниматься как параграфы
	                if ($para_tags) $_para_tags = array_flip($para_tags);
	            }
	        }#if
	
	        #обработка тагов
	        if ($is_html)
	        {
	            $_callback_type = 'strip_tags';
	            $s2 = preg_replace_callback($re_tags, array($this, __METHOD__), $s2);
	            $_callback_type = false;
	            if (preg_last_error() !== PREG_NO_ERROR)
	            {
	                $i = 999;
	                break;
	            }
	        }
	
	        if ($s === $s2) break;
	        $s = $s2; $i++;
	    }#while
	    if ($i >= $max) $s = strip_tags($s); #too many cycles for replace...
	
	    if ($is_format_spaces && strlen($s) !== $length)
	    {
	        #вырезаем дублирующие пробелы
	        $s = preg_replace('/\x20\x20++/sS', ' ', trim($s));
	        #вырезаем пробелы в начале и в конце строк
	        $s = str_replace(array("\r\n\x20", "\x20\r\n"), "\r\n", $s);
	        #заменяем 3 и более переносов строк на 2 переноса строк
	        #$s = preg_replace('/\r\n[\r\n]++/sS', "\r\n\r\n", $s);
	        $s = preg_replace('/[\r\n]{3,}+/sS', "\r\n\r\n", $s);
	    }
	    return $s;
	}	

	/**
	 * Конвертирует регистр букв в строке в кодировке UTF-8
	 */
	private function _ConvertCase($s, $mode)
	{
	    #таблица конвертации регистра
	    static $trans = array(
	        #en (английский латиница)
	        #CASE_UPPER => case_lower
	        "\x41" => "\x61", #A a
	        "\x42" => "\x62", #B b
	        "\x43" => "\x63", #C c
	        "\x44" => "\x64", #D d
	        "\x45" => "\x65", #E e
	        "\x46" => "\x66", #F f
	        "\x47" => "\x67", #G g
	        "\x48" => "\x68", #H h
	        "\x49" => "\x69", #I i
	        "\x4a" => "\x6a", #J j
	        "\x4b" => "\x6b", #K k
	        "\x4c" => "\x6c", #L l
	        "\x4d" => "\x6d", #M m
	        "\x4e" => "\x6e", #N n
	        "\x4f" => "\x6f", #O o
	        "\x50" => "\x70", #P p
	        "\x51" => "\x71", #Q q
	        "\x52" => "\x72", #R r
	        "\x53" => "\x73", #S s
	        "\x54" => "\x74", #T t
	        "\x55" => "\x75", #U u
	        "\x56" => "\x76", #V v
	        "\x57" => "\x77", #W w
	        "\x58" => "\x78", #X x
	        "\x59" => "\x79", #Y y
	        "\x5a" => "\x7a", #Z z
	
	        #ru (русский кириллица)
	        #CASE_UPPER => case_lower
	        "\xd0\x81" => "\xd1\x91", #Ё ё
	        "\xd0\x90" => "\xd0\xb0", #А а
	        "\xd0\x91" => "\xd0\xb1", #Б б
	        "\xd0\x92" => "\xd0\xb2", #В в
	        "\xd0\x93" => "\xd0\xb3", #Г г
	        "\xd0\x94" => "\xd0\xb4", #Д д
	        "\xd0\x95" => "\xd0\xb5", #Е е
	        "\xd0\x96" => "\xd0\xb6", #Ж ж
	        "\xd0\x97" => "\xd0\xb7", #З з
	        "\xd0\x98" => "\xd0\xb8", #И и
	        "\xd0\x99" => "\xd0\xb9", #Й й
	        "\xd0\x9a" => "\xd0\xba", #К к
	        "\xd0\x9b" => "\xd0\xbb", #Л л
	        "\xd0\x9c" => "\xd0\xbc", #М м
	        "\xd0\x9d" => "\xd0\xbd", #Н н
	        "\xd0\x9e" => "\xd0\xbe", #О о
	        "\xd0\x9f" => "\xd0\xbf", #П п
	
	        #CASE_UPPER => case_lower
	        "\xd0\xa0" => "\xd1\x80", #Р р
	        "\xd0\xa1" => "\xd1\x81", #С с
	        "\xd0\xa2" => "\xd1\x82", #Т т
	        "\xd0\xa3" => "\xd1\x83", #У у
	        "\xd0\xa4" => "\xd1\x84", #Ф ф
	        "\xd0\xa5" => "\xd1\x85", #Х х
	        "\xd0\xa6" => "\xd1\x86", #Ц ц
	        "\xd0\xa7" => "\xd1\x87", #Ч ч
	        "\xd0\xa8" => "\xd1\x88", #Ш ш
	        "\xd0\xa9" => "\xd1\x89", #Щ щ
	        "\xd0\xaa" => "\xd1\x8a", #Ъ ъ
	        "\xd0\xab" => "\xd1\x8b", #Ы ы
	        "\xd0\xac" => "\xd1\x8c", #Ь ь
	        "\xd0\xad" => "\xd1\x8d", #Э э
	        "\xd0\xae" => "\xd1\x8e", #Ю ю
	        "\xd0\xaf" => "\xd1\x8f", #Я я
	
	        #tt (татарский, башкирский кириллица)
	        #CASE_UPPER => case_lower
	        "\xd2\x96" => "\xd2\x97", #Ж ж с хвостиком    &#1174; => &#1175;
	        "\xd2\xa2" => "\xd2\xa3", #Н н с хвостиком    &#1186; => &#1187;
	        "\xd2\xae" => "\xd2\xaf", #Y y                &#1198; => &#1199;
	        "\xd2\xba" => "\xd2\xbb", #h h мягкое         &#1210; => &#1211;
	        "\xd3\x98" => "\xd3\x99", #Э э                &#1240; => &#1241;
	        "\xd3\xa8" => "\xd3\xa9", #О o перечеркнутое  &#1256; => &#1257;
	
	        #uk (украинский кириллица)
	        #CASE_UPPER => case_lower
	        "\xd2\x90" => "\xd2\x91",  #г с хвостиком
	        "\xd0\x84" => "\xd1\x94",  #э зеркальное отражение
	        "\xd0\x86" => "\xd1\x96",  #и с одной точкой
	        "\xd0\x87" => "\xd1\x97",  #и с двумя точками
	
	        #be (белорусский кириллица)
	        #CASE_UPPER => case_lower
	        "\xd0\x8e" => "\xd1\x9e",  #у с подковой над буквой
	
	        #tr,de,es (турецкий, немецкий, испанский, французский латиница)
	        #CASE_UPPER => case_lower
	        "\xc3\x84" => "\xc3\xa4", #a умляут          &#196; => &#228;  (турецкий)
	        "\xc3\x87" => "\xc3\xa7", #c с хвостиком     &#199; => &#231;  (турецкий, французский)
	        "\xc3\x91" => "\xc3\xb1", #n с тильдой       &#209; => &#241;  (турецкий, испанский)
	        "\xc3\x96" => "\xc3\xb6", #o умляут          &#214; => &#246;  (турецкий)
	        "\xc3\x9c" => "\xc3\xbc", #u умляут          &#220; => &#252;  (турецкий, французский)
	        "\xc4\x9e" => "\xc4\x9f", #g умляут          &#286; => &#287;  (турецкий)
	        "\xc4\xb0" => "\xc4\xb1", #i c точкой и без  &#304; => &#305;  (турецкий)
	        "\xc5\x9e" => "\xc5\x9f", #s с хвостиком     &#350; => &#351;  (турецкий)
	
	        #hr (хорватский латиница)
	        #CASE_UPPER => case_lower
	        "\xc4\x8c" => "\xc4\x8d",  #c с подковой над буквой
	        "\xc4\x86" => "\xc4\x87",  #c с ударением
	        "\xc4\x90" => "\xc4\x91",  #d перечеркнутое
	        "\xc5\xa0" => "\xc5\xa1",  #s с подковой над буквой
	        "\xc5\xbd" => "\xc5\xbe",  #z с подковой над буквой
	
	        #fr (французский латиница)
	        #CASE_UPPER => case_lower
	        "\xc3\x80" => "\xc3\xa0",  #a с ударением в др. сторону
	        "\xc3\x82" => "\xc3\xa2",  #a с крышкой
	        "\xc3\x86" => "\xc3\xa6",  #ae совмещенное
	        "\xc3\x88" => "\xc3\xa8",  #e с ударением в др. сторону
	        "\xc3\x89" => "\xc3\xa9",  #e с ударением
	        "\xc3\x8a" => "\xc3\xaa",  #e с крышкой
	        "\xc3\x8b" => "\xc3\xab",  #ё
	        "\xc3\x8e" => "\xc3\xae",  #i с крышкой
	        "\xc3\x8f" => "\xc3\xaf",  #i умляут
	        "\xc3\x94" => "\xc3\xb4",  #o с крышкой
	        "\xc5\x92" => "\xc5\x93",  #ce совмещенное
	        "\xc3\x99" => "\xc3\xb9",  #u с ударением в др. сторону
	        "\xc3\x9b" => "\xc3\xbb",  #u с крышкой
	        "\xc5\xb8" => "\xc3\xbf",  #y умляут
	
	        #xx (другой язык)
	        #CASE_UPPER => case_lower
	        #"" => "",  #
	
	    );
	    #d($trans);
	
	    if ($mode == CASE_UPPER)
	    {
	        if ($this->_isMBString)   return mb_strtoupper($s);
	        if (preg_match('/^[\x00-\x7e]*$/', $s)) return strtoupper($s); #может, так быстрее?
	        return strtr($s, array_flip($trans));
	    }
	    elseif ($mode == CASE_LOWER)
	    {
	        if ($this->_isMBString)   return mb_strtolower($s, 'utf-8');
	        if (preg_match('/^[\x00-\x7e]*$/', $s)) return strtolower($s); #может, так быстрее?
	        return strtr($s, $trans);
	    }
	    else
	    {
	        throw new Exception('Parameter 2 should be a constant of CASE_LOWER or CASE_UPPER!');
	    }
	    
	    return $s;
	}

	public function ToLower($str)
	{
	    return $this->_ConvertCase($str, CASE_LOWER);
	}	

	public function ToUpper($str)
	{
	    return $this->_ConvertCase($str, CASE_UPPER);
	}		
	
	/**
	 * Implementation strcasecmp() function for UTF-8 encoding string.
	 */
	public function CaseCmp($s1, $s2)
	{
	    return strcmp($this->ToLower($s1), $this->ToLower($s2));
	}

	/**
	 * Converts a UNICODE codepoint to a UTF-8 character
	 */
	public function chr($cp)
	{
	    static $cache = array();
	    $cp = intval($cp);
	    if (array_key_exists($cp, $cache)) return $cache[$cp]; #speed improve
	
	    if ($cp <= 0x7f)     return $cache[$cp] = chr($cp);
	    if ($cp <= 0x7ff)    return $cache[$cp] = chr(0xc0 | ($cp >> 6))  .
	                                              chr(0x80 | ($cp & 0x3f));
	    if ($cp <= 0xffff)   return $cache[$cp] = chr(0xe0 | ($cp >> 12)) .
	                                              chr(0x80 | (($cp >> 6) & 0x3f)) .
	                                              chr(0x80 | ($cp & 0x3f));
	    if ($cp <= 0x10ffff) return $cache[$cp] = chr(0xf0 | ($cp >> 18)) .
	                                              chr(0x80 | (($cp >> 12) & 0x3f)) .
	                                              chr(0x80 | (($cp >> 6) & 0x3f)) .
	                                              chr(0x80 | ($cp & 0x3f));
	    #U+FFFD REPLACEMENT CHARACTER
	    return $cache[$cp] = "\xEF\xBF\xBD";
	}

	/**
	 * Converts a UTF-8 character to a UNICODE codepoint
	 */
	public function ord($char)
	{
	    static $cache = array();
	    if (array_key_exists($char, $cache)) return $cache[$char]; #speed improve
	
	    switch (strlen($char))
	    {
	        case 1 : return $cache[$char] = ord($char);
	        case 2 : return $cache[$char] = (ord($char{1}) & 63) |
	                                        ((ord($char{0}) & 31) << 6);
	        case 3 : return $cache[$char] = (ord($char{2}) & 63) |
	                                        ((ord($char{1}) & 63) << 6) |
	                                        ((ord($char{0}) & 15) << 12);
	        case 4 : return $cache[$char] = (ord($char{3}) & 63) |
	                                        ((ord($char{2}) & 63) << 6) |
	                                        ((ord($char{1}) & 63) << 12) |
	                                        ((ord($char{0}) & 7)  << 18);
	        default :
	            throw new Exception('Character is not UTF-8!');
	    }#switch
	}	
	
	/**
	 * Implementation str_split() function for UTF-8 encoding string.
	 */
	public function Split(/*string*/ $string, /*int*/ $length = null)
	{
	    if (! is_string($string)) throw new Exception('A string type expected in first parameter, ' . gettype($string) . ' given!');
	    $length = ($length === null) ? 1 : intval($length);
	    if ($length < 1) return false;
	    #there are limits in regexp for {min,max}!
	    if ($length < 100)
	    {
	        preg_match_all('/(?>[\x09\x0A\x0D\x20-\x7E]           # ASCII
	                          | [\xC2-\xDF][\x80-\xBF]            # non-overlong 2-byte
	                          |  \xE0[\xA0-\xBF][\x80-\xBF]       # excluding overlongs
	                          | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
	                          |  \xED[\x80-\x9F][\x80-\xBF]       # excluding surrogates
	                          |  \xF0[\x90-\xBF][\x80-\xBF]{2}    # planes 1-3
	                          | [\xF1-\xF3][\x80-\xBF]{3}         # planes 4-15
	                          |  \xF4[\x80-\x8F][\x80-\xBF]{2}    # plane 16
	                          #| (.)                               # catch bad bytes
	                         ){1,' . $length . '}
	                        /xsS', $string, $m);
	        $a =& $m[0];
	    }
	    else
	    {
	        preg_match_all('/(?>[\x09\x0A\x0D\x20-\x7E]           # ASCII
	                          | [\xC2-\xDF][\x80-\xBF]            # non-overlong 2-byte
	                          |  \xE0[\xA0-\xBF][\x80-\xBF]       # excluding overlongs
	                          | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
	                          |  \xED[\x80-\x9F][\x80-\xBF]       # excluding surrogates
	                          |  \xF0[\x90-\xBF][\x80-\xBF]{2}    # planes 1-3
	                          | [\xF1-\xF3][\x80-\xBF]{3}         # planes 4-15
	                          |  \xF4[\x80-\x8F][\x80-\xBF]{2}    # plane 16
	                          #| (.)                               # catch bad bytes
	                         )
	                        /xsS', $string, $m);
	        $a = array();
	        for ($i = 0, $c = count($m[0]); $i < $c; $i += $length) $a[] = implode('', array_slice($m[0], $i, $length));
	    }
	    #check UTF-8 data
	    $distance = strlen($string) - strlen(implode('', $a));
	    if ($distance > 0)
	    {
	        throw new Exception('Charset is not UTF-8, total ' . $distance . ' unknown bytes found!');
	    }
	    return $a;
	}	
	
	/**
	 * Implementation chunk_split() function for UTF-8 encoding string.
	 */
	public function ChunkSplit(/*string*/ $string, /*int*/ $length = null, /*string*/ $glue = null)
	{
	    if (! is_string($string)) throw new Exception('A string type expected in first parameter, ' . gettype($string) . ' given!');
	    $length = intval($length);
	    $glue   = strval($glue);
	    if ($length < 1) $length = 76;
	    if ($glue === '') $glue = "\r\n";
	    if (! is_array($a = $this->Split($string, $length))) return false;
	    return implode($glue, $a);
	}	
	
	/**
	 * Implementation strrev() function for UTF-8 encoding string
	*/
	public function Rev($string)
	{
	    if (! is_array($a = $this->Split($string))) return false;
	    return implode('', array_reverse($a));
	}

	/**
	 * Implementation substr_replace() function for UTF-8 encoding string.
	 */
	public function SubStrReplace(/*string*/ $string,
	                             /*string*/ $replacement,
	                             /*int*/    $start,
	                             /*int*/    $length = null)
	{
	    if (! is_array($a = $this->Split($string))) return false;
	    array_splice($a, $start, $length, $replacement);
	    return implode('', $a);
	}

	/**
	 * Implementation ucfirst() function for UTF-8 encoding string.
	 * Преобразует первый символ строки в кодировке UTF-8 в верхний регистр.
	 */
	public function UcFirst($s, $is_other_to_lowercase = true)
	{
	    if ($s === '' or ! is_string($s)) return $s;
	    if (preg_match('/^(.)(.*)$/us', $s, $m) === false) return false;
	    return $this->ToUpper($m[1]) . ($is_other_to_lowercase ? $this->ToLower($m[2]) : $m[2]);
	}	
	
	/**
	 * Implementation ucwords() function for UTF-8 encoding string.
	 * Преобразует в верхний регистр первый символ каждого слова в строке в кодировке UTF-8,
	 * остальные символы каждого слова преобразуются в нижний регистр.
	 * Эта функция считает словами последовательности символов, разделенных пробелом, переводом строки, возвратом каретки, горизонтальной табуляцией, неразрывным пробелом.
	 */
	public function UcWords($s)
	{
	    $words = preg_split('/([\x20\r\n\t]++|\xc2\xa0)/sS', $s, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
	    foreach ($words as $k => $word) $words[$k] = $this->UcFirst($word);
	    return implode('', $words);
	}

	/**
	 * Функция декодирует строку в формате %uXXXX в строку формата UTF-8.
	 * Функция работает без использования библиотеки iconv!
	 *
	 * Функция используется для декодирования данных типа "%u0442%u0435%u0441%u0442",
	 * закодированных устаревшей функцией javascript://encode().
	 * Рекомендуется использовать функцию javascript://encodeURIComponent().
	 *
	 * ЗАМЕЧАНИЕ
	 * Устаревший формат %uXXXX позволяет использовать юникод только из диапазона UCS-2, т.е. от U+0 до U+FFFF
	 */
	public function Unescape($s, $is_rawurlencode = false)
	{
	    if (! is_string($s) || strpos($s, '%u') === false) return $s; #use strpos() for speed improving
	    return preg_replace_callback('/%u([\da-fA-F]{4})/sS', array($this, $is_rawurlencode ? '_UnescapeRawurlencode' : '_Unescape'), $s);
	}
	
	private function _Unescape(array $m)
	{
	    $codepoint = hexdec($m[1]);
	    return $this->Chr($codepoint);
	}
	
	private function _UnescapeRawurlencode(array $m)
	{
	    $codepoint = hexdec($m[1]);
	    return rawurlencode($this->Chr($codepoint));
	}

	/**
	 * Рекурсивный вариант utf8_unescape()
	 */
	public function UnescapeRecursive($data, $is_rawurlencode = false)
	{
	    if (is_array($data))
	    {
	        $d = array();
	        foreach ($data as $k => &$v) $d[$this->Unescape($k, $is_rawurlencode)] = call_user_func(array($this, 'UnescapeRecursive'), $v, $is_rawurlencode);
	        return $d;
	    }
	    else return $this->Unescape($data, $is_rawurlencode);
	}

	/**
	 * Call preg_match_all() and convert byte offsets into (UTF-8) character offsets for PREG_OFFSET_CAPTURE flag.
	 * This is regardless of whether you use /u modifier.
	 */
	public function PregMatchAll(
	    /*string*/ $pattern,
	    /*string*/ $subject,
	    /*array*/  &$matches,
	    /*int*/    $flags = PREG_PATTERN_ORDER,
	    /*int*/    $char_offset = 0
	)
	{
	    if ($char_offset)
	    {
	        $byte_offset = strlen($this->substr($subject, 0, $char_offset));
	    }
	    else $byte_offset = $char_offset;
	
	    if (preg_match_all($pattern, $subject, $matches, $flags, $byte_offset) === false) return false;
	
	    if ($flags & PREG_OFFSET_CAPTURE)
	    {
	        foreach($matches as &$match)
	        {
	            foreach($match as &$a) $a[1] = $this->len(substr($subject, 0, $a[1]));
	        }
	    }
	
	    return $return;
	}	
	
	/**
	 * Implementation substr() function for UTF-8 encoding string.
	 */
	public function SubStr($str, $offset, $length = null)
	{
	    if ($this->_isMBString) return mb_substr($str, $offset, $length); #(PHP 4 >= 4.0.6, PHP 5)
	    
	    if (($length >= 0) && ($length < 65535) && ($offset >= 0) && ($offset < 65535))
	    {
	    	return preg_replace('/^.{' .$offset. '}(.{'.$length.'}).*$/uSs', '$1', $str);
	    }
	    
	    if (!is_array($a = $this->Split($str))) return false;
	    if ($length !== null) $a = array_slice($a, $offset, $length);
	    else                  $a = array_slice($a, $offset);
	    return implode('', $a);
	}
	
	/**
	* количество символов в строке 
	*/
	public function Len($str)
	{
        if ($this->_isMBString) return mb_strlen($str);
        return strlen(utf8_decode($str));
	}

	/**
	* количество байт в строке 
	*/	
	public function Size($str)
	{
        return strlen($str);	    
	}

    private $_entities = array(
      #Latin-1 Entities:
        '&nbsp;'   => "\xc2\xa0",  #no-break space = non-breaking space
        '&iexcl;'  => "\xc2\xa1",  #inverted exclamation mark
        '&cent;'   => "\xc2\xa2",  #cent sign
        '&pound;'  => "\xc2\xa3",  #pound sign
        '&curren;' => "\xc2\xa4",  #currency sign
        '&yen;'    => "\xc2\xa5",  #yen sign = yuan sign
        '&brvbar;' => "\xc2\xa6",  #broken bar = broken vertical bar
        '&sect;'   => "\xc2\xa7",  #section sign
        '&uml;'    => "\xc2\xa8",  #diaeresis = spacing diaeresis
        '&copy;'   => "\xc2\xa9",  #copyright sign
        '&ordf;'   => "\xc2\xaa",  #feminine ordinal indicator
        '&laquo;'  => "\xc2\xab",  #left-pointing double angle quotation mark = left pointing guillemet («)
        '&not;'    => "\xc2\xac",  #not sign
        '&shy;'    => "\xc2\xad",  #soft hyphen = discretionary hyphen
        '&reg;'    => "\xc2\xae",  #registered sign = registered trade mark sign
        '&macr;'   => "\xc2\xaf",  #macron = spacing macron = overline = APL overbar
        '&deg;'    => "\xc2\xb0",  #degree sign
        '&plusmn;' => "\xc2\xb1",  #plus-minus sign = plus-or-minus sign
        '&sup2;'   => "\xc2\xb2",  #superscript two = superscript digit two = squared
        '&sup3;'   => "\xc2\xb3",  #superscript three = superscript digit three = cubed
        '&acute;'  => "\xc2\xb4",  #acute accent = spacing acute
        '&micro;'  => "\xc2\xb5",  #micro sign
        '&para;'   => "\xc2\xb6",  #pilcrow sign = paragraph sign
        '&middot;' => "\xc2\xb7",  #middle dot = Georgian comma = Greek middle dot
        '&cedil;'  => "\xc2\xb8",  #cedilla = spacing cedilla
        '&sup1;'   => "\xc2\xb9",  #superscript one = superscript digit one
        '&ordm;'   => "\xc2\xba",  #masculine ordinal indicator
        '&raquo;'  => "\xc2\xbb",  #right-pointing double angle quotation mark = right pointing guillemet (»)
        '&frac14;' => "\xc2\xbc",  #vulgar fraction one quarter = fraction one quarter
        '&frac12;' => "\xc2\xbd",  #vulgar fraction one half = fraction one half
        '&frac34;' => "\xc2\xbe",  #vulgar fraction three quarters = fraction three quarters
        '&iquest;' => "\xc2\xbf",  #inverted question mark = turned question mark
      #Latin capital letter
        '&Agrave;' => "\xc3\x80",  #Latin capital letter A with grave = Latin capital letter A grave
        '&Aacute;' => "\xc3\x81",  #Latin capital letter A with acute
        '&Acirc;'  => "\xc3\x82",  #Latin capital letter A with circumflex
        '&Atilde;' => "\xc3\x83",  #Latin capital letter A with tilde
        '&Auml;'   => "\xc3\x84",  #Latin capital letter A with diaeresis
        '&Aring;'  => "\xc3\x85",  #Latin capital letter A with ring above = Latin capital letter A ring
        '&AElig;'  => "\xc3\x86",  #Latin capital letter AE = Latin capital ligature AE
        '&Ccedil;' => "\xc3\x87",  #Latin capital letter C with cedilla
        '&Egrave;' => "\xc3\x88",  #Latin capital letter E with grave
        '&Eacute;' => "\xc3\x89",  #Latin capital letter E with acute
        '&Ecirc;'  => "\xc3\x8a",  #Latin capital letter E with circumflex
        '&Euml;'   => "\xc3\x8b",  #Latin capital letter E with diaeresis
        '&Igrave;' => "\xc3\x8c",  #Latin capital letter I with grave
        '&Iacute;' => "\xc3\x8d",  #Latin capital letter I with acute
        '&Icirc;'  => "\xc3\x8e",  #Latin capital letter I with circumflex
        '&Iuml;'   => "\xc3\x8f",  #Latin capital letter I with diaeresis
        '&ETH;'    => "\xc3\x90",  #Latin capital letter ETH
        '&Ntilde;' => "\xc3\x91",  #Latin capital letter N with tilde
        '&Ograve;' => "\xc3\x92",  #Latin capital letter O with grave
        '&Oacute;' => "\xc3\x93",  #Latin capital letter O with acute
        '&Ocirc;'  => "\xc3\x94",  #Latin capital letter O with circumflex
        '&Otilde;' => "\xc3\x95",  #Latin capital letter O with tilde
        '&Ouml;'   => "\xc3\x96",  #Latin capital letter O with diaeresis
        '&times;'  => "\xc3\x97",  #multiplication sign
        '&Oslash;' => "\xc3\x98",  #Latin capital letter O with stroke = Latin capital letter O slash
        '&Ugrave;' => "\xc3\x99",  #Latin capital letter U with grave
        '&Uacute;' => "\xc3\x9a",  #Latin capital letter U with acute
        '&Ucirc;'  => "\xc3\x9b",  #Latin capital letter U with circumflex
        '&Uuml;'   => "\xc3\x9c",  #Latin capital letter U with diaeresis
        '&Yacute;' => "\xc3\x9d",  #Latin capital letter Y with acute
        '&THORN;'  => "\xc3\x9e",  #Latin capital letter THORN
      #Latin small letter
        '&szlig;'  => "\xc3\x9f",  #Latin small letter sharp s = ess-zed
        '&agrave;' => "\xc3\xa0",  #Latin small letter a with grave = Latin small letter a grave
        '&aacute;' => "\xc3\xa1",  #Latin small letter a with acute
        '&acirc;'  => "\xc3\xa2",  #Latin small letter a with circumflex
        '&atilde;' => "\xc3\xa3",  #Latin small letter a with tilde
        '&auml;'   => "\xc3\xa4",  #Latin small letter a with diaeresis
        '&aring;'  => "\xc3\xa5",  #Latin small letter a with ring above = Latin small letter a ring
        '&aelig;'  => "\xc3\xa6",  #Latin small letter ae = Latin small ligature ae
        '&ccedil;' => "\xc3\xa7",  #Latin small letter c with cedilla
        '&egrave;' => "\xc3\xa8",  #Latin small letter e with grave
        '&eacute;' => "\xc3\xa9",  #Latin small letter e with acute
        '&ecirc;'  => "\xc3\xaa",  #Latin small letter e with circumflex
        '&euml;'   => "\xc3\xab",  #Latin small letter e with diaeresis
        '&igrave;' => "\xc3\xac",  #Latin small letter i with grave
        '&iacute;' => "\xc3\xad",  #Latin small letter i with acute
        '&icirc;'  => "\xc3\xae",  #Latin small letter i with circumflex
        '&iuml;'   => "\xc3\xaf",  #Latin small letter i with diaeresis
        '&eth;'    => "\xc3\xb0",  #Latin small letter eth
        '&ntilde;' => "\xc3\xb1",  #Latin small letter n with tilde
        '&ograve;' => "\xc3\xb2",  #Latin small letter o with grave
        '&oacute;' => "\xc3\xb3",  #Latin small letter o with acute
        '&ocirc;'  => "\xc3\xb4",  #Latin small letter o with circumflex
        '&otilde;' => "\xc3\xb5",  #Latin small letter o with tilde
        '&ouml;'   => "\xc3\xb6",  #Latin small letter o with diaeresis
        '&divide;' => "\xc3\xb7",  #division sign
        '&oslash;' => "\xc3\xb8",  #Latin small letter o with stroke = Latin small letter o slash
        '&ugrave;' => "\xc3\xb9",  #Latin small letter u with grave
        '&uacute;' => "\xc3\xba",  #Latin small letter u with acute
        '&ucirc;'  => "\xc3\xbb",  #Latin small letter u with circumflex
        '&uuml;'   => "\xc3\xbc",  #Latin small letter u with diaeresis
        '&yacute;' => "\xc3\xbd",  #Latin small letter y with acute
        '&thorn;'  => "\xc3\xbe",  #Latin small letter thorn
        '&yuml;'   => "\xc3\xbf",  #Latin small letter y with diaeresis
      #Symbols and Greek Letters:
        '&fnof;'    => "\xc6\x92",  #Latin small f with hook = function = florin
        '&Alpha;'   => "\xce\x91",  #Greek capital letter alpha
        '&Beta;'    => "\xce\x92",  #Greek capital letter beta
        '&Gamma;'   => "\xce\x93",  #Greek capital letter gamma
        '&Delta;'   => "\xce\x94",  #Greek capital letter delta
        '&Epsilon;' => "\xce\x95",  #Greek capital letter epsilon
        '&Zeta;'    => "\xce\x96",  #Greek capital letter zeta
        '&Eta;'     => "\xce\x97",  #Greek capital letter eta
        '&Theta;'   => "\xce\x98",  #Greek capital letter theta
        '&Iota;'    => "\xce\x99",  #Greek capital letter iota
        '&Kappa;'   => "\xce\x9a",  #Greek capital letter kappa
        '&Lambda;'  => "\xce\x9b",  #Greek capital letter lambda
        '&Mu;'      => "\xce\x9c",  #Greek capital letter mu
        '&Nu;'      => "\xce\x9d",  #Greek capital letter nu
        '&Xi;'      => "\xce\x9e",  #Greek capital letter xi
        '&Omicron;' => "\xce\x9f",  #Greek capital letter omicron
        '&Pi;'      => "\xce\xa0",  #Greek capital letter pi
        '&Rho;'     => "\xce\xa1",  #Greek capital letter rho
        '&Sigma;'   => "\xce\xa3",  #Greek capital letter sigma
        '&Tau;'     => "\xce\xa4",  #Greek capital letter tau
        '&Upsilon;' => "\xce\xa5",  #Greek capital letter upsilon
        '&Phi;'     => "\xce\xa6",  #Greek capital letter phi
        '&Chi;'     => "\xce\xa7",  #Greek capital letter chi
        '&Psi;'     => "\xce\xa8",  #Greek capital letter psi
        '&Omega;'   => "\xce\xa9",  #Greek capital letter omega
        '&alpha;'   => "\xce\xb1",  #Greek small letter alpha
        '&beta;'    => "\xce\xb2",  #Greek small letter beta
        '&gamma;'   => "\xce\xb3",  #Greek small letter gamma
        '&delta;'   => "\xce\xb4",  #Greek small letter delta
        '&epsilon;' => "\xce\xb5",  #Greek small letter epsilon
        '&zeta;'    => "\xce\xb6",  #Greek small letter zeta
        '&eta;'     => "\xce\xb7",  #Greek small letter eta
        '&theta;'   => "\xce\xb8",  #Greek small letter theta
        '&iota;'    => "\xce\xb9",  #Greek small letter iota
        '&kappa;'   => "\xce\xba",  #Greek small letter kappa
        '&lambda;'  => "\xce\xbb",  #Greek small letter lambda
        '&mu;'      => "\xce\xbc",  #Greek small letter mu
        '&nu;'      => "\xce\xbd",  #Greek small letter nu
        '&xi;'      => "\xce\xbe",  #Greek small letter xi
        '&omicron;' => "\xce\xbf",  #Greek small letter omicron
        '&pi;'      => "\xcf\x80",  #Greek small letter pi
        '&rho;'     => "\xcf\x81",  #Greek small letter rho
        '&sigmaf;'  => "\xcf\x82",  #Greek small letter final sigma
        '&sigma;'   => "\xcf\x83",  #Greek small letter sigma
        '&tau;'     => "\xcf\x84",  #Greek small letter tau
        '&upsilon;' => "\xcf\x85",  #Greek small letter upsilon
        '&phi;'     => "\xcf\x86",  #Greek small letter phi
        '&chi;'     => "\xcf\x87",  #Greek small letter chi
        '&psi;'     => "\xcf\x88",  #Greek small letter psi
        '&omega;'   => "\xcf\x89",  #Greek small letter omega
        '&thetasym;'=> "\xcf\x91",  #Greek small letter theta symbol
        '&upsih;'   => "\xcf\x92",  #Greek upsilon with hook symbol
        '&piv;'     => "\xcf\x96",  #Greek pi symbol

        '&bull;'    => "\xe2\x80\xa2",  #bullet = black small circle
        '&hellip;'  => "\xe2\x80\xa6",  #horizontal ellipsis = three dot leader
        '&prime;'   => "\xe2\x80\xb2",  #prime = minutes = feet (для обозначения минут и футов)
        '&Prime;'   => "\xe2\x80\xb3",  #double prime = seconds = inches (для обозначения секунд и дюймов).
        '&oline;'   => "\xe2\x80\xbe",  #overline = spacing overscore
        '&frasl;'   => "\xe2\x81\x84",  #fraction slash
        '&weierp;'  => "\xe2\x84\x98",  #script capital P = power set = Weierstrass p
        '&image;'   => "\xe2\x84\x91",  #blackletter capital I = imaginary part
        '&real;'    => "\xe2\x84\x9c",  #blackletter capital R = real part symbol
        '&trade;'   => "\xe2\x84\xa2",  #trade mark sign
        '&alefsym;' => "\xe2\x84\xb5",  #alef symbol = first transfinite cardinal
        '&larr;'    => "\xe2\x86\x90",  #leftwards arrow
        '&uarr;'    => "\xe2\x86\x91",  #upwards arrow
        '&rarr;'    => "\xe2\x86\x92",  #rightwards arrow
        '&darr;'    => "\xe2\x86\x93",  #downwards arrow
        '&harr;'    => "\xe2\x86\x94",  #left right arrow
        '&crarr;'   => "\xe2\x86\xb5",  #downwards arrow with corner leftwards = carriage return
        '&lArr;'    => "\xe2\x87\x90",  #leftwards double arrow
        '&uArr;'    => "\xe2\x87\x91",  #upwards double arrow
        '&rArr;'    => "\xe2\x87\x92",  #rightwards double arrow
        '&dArr;'    => "\xe2\x87\x93",  #downwards double arrow
        '&hArr;'    => "\xe2\x87\x94",  #left right double arrow
        '&forall;'  => "\xe2\x88\x80",  #for all
        '&part;'    => "\xe2\x88\x82",  #partial differential
        '&exist;'   => "\xe2\x88\x83",  #there exists
        '&empty;'   => "\xe2\x88\x85",  #empty set = null set = diameter
        '&nabla;'   => "\xe2\x88\x87",  #nabla = backward difference
        '&isin;'    => "\xe2\x88\x88",  #element of
        '&notin;'   => "\xe2\x88\x89",  #not an element of
        '&ni;'      => "\xe2\x88\x8b",  #contains as member
        '&prod;'    => "\xe2\x88\x8f",  #n-ary product = product sign
        '&sum;'     => "\xe2\x88\x91",  #n-ary sumation
        '&minus;'   => "\xe2\x88\x92",  #minus sign
        '&lowast;'  => "\xe2\x88\x97",  #asterisk operator
        '&radic;'   => "\xe2\x88\x9a",  #square root = radical sign
        '&prop;'    => "\xe2\x88\x9d",  #proportional to
        '&infin;'   => "\xe2\x88\x9e",  #infinity
        '&ang;'     => "\xe2\x88\xa0",  #angle
        '&and;'     => "\xe2\x88\xa7",  #logical and = wedge
        '&or;'      => "\xe2\x88\xa8",  #logical or = vee
        '&cap;'     => "\xe2\x88\xa9",  #intersection = cap
        '&cup;'     => "\xe2\x88\xaa",  #union = cup
        '&int;'     => "\xe2\x88\xab",  #integral
        '&there4;'  => "\xe2\x88\xb4",  #therefore
        '&sim;'     => "\xe2\x88\xbc",  #tilde operator = varies with = similar to
        '&cong;'    => "\xe2\x89\x85",  #approximately equal to
        '&asymp;'   => "\xe2\x89\x88",  #almost equal to = asymptotic to
        '&ne;'      => "\xe2\x89\xa0",  #not equal to
        '&equiv;'   => "\xe2\x89\xa1",  #identical to
        '&le;'      => "\xe2\x89\xa4",  #less-than or equal to
        '&ge;'      => "\xe2\x89\xa5",  #greater-than or equal to
        '&sub;'     => "\xe2\x8a\x82",  #subset of
        '&sup;'     => "\xe2\x8a\x83",  #superset of
        '&nsub;'    => "\xe2\x8a\x84",  #not a subset of
        '&sube;'    => "\xe2\x8a\x86",  #subset of or equal to
        '&supe;'    => "\xe2\x8a\x87",  #superset of or equal to
        '&oplus;'   => "\xe2\x8a\x95",  #circled plus = direct sum
        '&otimes;'  => "\xe2\x8a\x97",  #circled times = vector product
        '&perp;'    => "\xe2\x8a\xa5",  #up tack = orthogonal to = perpendicular
        '&sdot;'    => "\xe2\x8b\x85",  #dot operator
        '&lceil;'   => "\xe2\x8c\x88",  #left ceiling = APL upstile
        '&rceil;'   => "\xe2\x8c\x89",  #right ceiling
        '&lfloor;'  => "\xe2\x8c\x8a",  #left floor = APL downstile
        '&rfloor;'  => "\xe2\x8c\x8b",  #right floor
        '&lang;'    => "\xe2\x8c\xa9",  #left-pointing angle bracket = bra
        '&rang;'    => "\xe2\x8c\xaa",  #right-pointing angle bracket = ket
        '&loz;'     => "\xe2\x97\x8a",  #lozenge
        '&spades;'  => "\xe2\x99\xa0",  #black spade suit
        '&clubs;'   => "\xe2\x99\xa3",  #black club suit = shamrock
        '&hearts;'  => "\xe2\x99\xa5",  #black heart suit = valentine
        '&diams;'   => "\xe2\x99\xa6",  #black diamond suit
      #Other Special Characters:
        '&OElig;'  => "\xc5\x92",  #Latin capital ligature OE
        '&oelig;'  => "\xc5\x93",  #Latin small ligature oe
        '&Scaron;' => "\xc5\xa0",  #Latin capital letter S with caron
        '&scaron;' => "\xc5\xa1",  #Latin small letter s with caron
        '&Yuml;'   => "\xc5\xb8",  #Latin capital letter Y with diaeresis
        '&circ;'   => "\xcb\x86",  #modifier letter circumflex accent
        '&tilde;'  => "\xcb\x9c",  #small tilde
        '&ensp;'   => "\xe2\x80\x82",  #en space
        '&emsp;'   => "\xe2\x80\x83",  #em space
        '&thinsp;' => "\xe2\x80\x89",  #thin space
        '&zwnj;'   => "\xe2\x80\x8c",  #zero width non-joiner
        '&zwj;'    => "\xe2\x80\x8d",  #zero width joiner
        '&lrm;'    => "\xe2\x80\x8e",  #left-to-right mark
        '&rlm;'    => "\xe2\x80\x8f",  #right-to-left mark
        '&ndash;'  => "\xe2\x80\x93",  #en dash
        '&mdash;'  => "\xe2\x80\x94",  #em dash
        '&lsquo;'  => "\xe2\x80\x98",  #left single quotation mark
        '&rsquo;'  => "\xe2\x80\x99",  #right single quotation mark (and apostrophe!)
        '&sbquo;'  => "\xe2\x80\x9a",  #single low-9 quotation mark
        '&ldquo;'  => "\xe2\x80\x9c",  #left double quotation mark
        '&rdquo;'  => "\xe2\x80\x9d",  #right double quotation mark
        '&bdquo;'  => "\xe2\x80\x9e",  #double low-9 quotation mark
        '&dagger;' => "\xe2\x80\xa0",  #dagger
        '&Dagger;' => "\xe2\x80\xa1",  #double dagger
        '&permil;' => "\xe2\x80\xb0",  #per mille sign
        '&lsaquo;' => "\xe2\x80\xb9",  #single left-pointing angle quotation mark
        '&rsaquo;' => "\xe2\x80\xba",  #single right-pointing angle quotation mark
        '&euro;'   => "\xe2\x82\xac",  #euro sign
    );	
	
	public function HTMLEntityDecode($s, $is_htmlspecialchars = false)
	{
	    #оптимизация скорости
	    if (strlen($s) < 4  #по минимальной длине сущности - 4 байта: &#d; &xx;
	        || ($pos = strpos($s, '&') === false) || strpos($s, ';', $pos) === false) return $s;
	    $table = $this->_entities;
	    $htmlspecialchars = array(
	        '&quot;' => "\x22",  #quotation mark = APL quote (") &#34;
	        '&amp;'  => "\x26",  #ampersand                  (&) &#38;
	        '&lt;'   => "\x3c",  #less-than sign             (<) &#60;
	        '&gt;'   => "\x3e",  #greater-than sign          (>) &#62;
	    );
	
	    if ($is_htmlspecialchars) $table += $htmlspecialchars;
	
	    #заменяем именованные сущности:
	    #оптимизация скорости: заменяем только те сущности, которые используются в html коде!
	    #эта часть кода работает быстрее, чем $s = strtr($s, $table);
	    preg_match_all('/&[a-zA-Z]++\d*+;/sS', $s, $m, null, $pos);
	    foreach (array_unique($m[0]) as $entity)
	    {
	        if (array_key_exists($entity, $table)) $s = str_replace($entity, $table[$entity], $s);
	    }#foreach
	
	    if (($pos = strpos($s, '&#')) !== false)  #speed optimization
	    {
	        #заменяем числовые dec и hex сущности:
	        $htmlspecialchars_flip = array_flip($htmlspecialchars);
	        $s = preg_replace('/&#((x)[\da-fA-F]{1,6}+|\d{1,7}+);/seS',  #1,114,112 sumbols total in UTF-16
	                          '(array_key_exists($char = pack("C", $codepoint = ("$2") ? hexdec("$1") : "$1"),
	                                             $htmlspecialchars_flip
	                                            )
	                            && ! $is_htmlspecialchars
	                           ) ? $htmlspecialchars_flip[$char]
	                             : $this->chr($codepoint)', $s, -1, $pos);
	    }
	    return $s;
	}
	
	public function HTMLEntityEncode($s)
	{
	    $table = array_flip($this->_entities);
	
	    #заменяем utf8-символы на именованные сущности:
	    #оптимизация скорости: заменяем только те символы, которые используются в html коде!
	    preg_match_all('/  [\xc2\xc3\xc5\xc6\xcb\xce\xcf][\x80-\xbf]  #2 bytes
	                     | \xe2[\x80-\x99][\x82-\xac]                 #3 bytes
	                     /sxS', $s, $m);
	    foreach (array_unique($m[0]) as $char)
	    {
	        if (array_key_exists($char, $table)) $s = str_replace($char, $table[$char], $s);
	    }#foreach
	
	    return $s;    	    
	}

	public function HTMLSpecialChars($str, $quote_style = null)
	{
        return htmlspecialchars($str, $quote_style, 'UTF-8'); 	    
	}
	
	public function HTMLSpecialCharsDecode($str, $quote_style = null)
	{
        return htmlspecialchars_decode($str, $quote_style); 	    
	}

	public function Replace($search, $replace, $subject, &$count = null)
	{
        return str_replace($search, $replace, $subject, $count);	    
	}
	
	/**
	 * Implementation strpos() function for UTF-8 encoding string
	 */
	public function Pos($haystack, $needle, $offset = null)
	{
	    if ($offset === null or $offset < 0) $offset = 0;
	    
	    if ($this->_isMBString) return mb_strpos($haystack, $needle, $offset);

	    $byte_pos = $offset;
	    do if (($byte_pos = strpos($haystack, $needle, $byte_pos)) === false) return false;
	    while (($char_pos = $this->len(substr($haystack, 0, $byte_pos++))) < $offset);
	    return $char_pos;
	}	
	
	public function StrStr($haystack, $needle)
	{
		return substr($haystack, strpos($haystack, $needle));  
	}
	
	/**
	* Unicode aware replacement for ltrim()
	*/
	public function ltrim($str, $charlist='')
	{
		if($charlist == '') return ltrim($str);
		//quote charlist for use in a characterclass
		$charlist = preg_replace('!([\\\\\\-\\]\\[/])!','\\\$1}',$charlist);
		return preg_replace('/^['.$charlist.']+/u','',$str);
	}
	
	/**
	* Unicode aware replacement for rtrim()
	*/
	public function rtrim($str, $charlist='')
	{
		if($charlist == '') return rtrim($str);
		//quote charlist for use in a characterclass
		$charlist = preg_replace('!([\\\\\\-\\]\\[/])!','\\\$1}',$charlist);
		return preg_replace('/['.$charlist.']+$/u','',$str);
	}
	
	/**
	* Unicode aware replacement for trim()
	*/
	public function trim($str, $charlist='') 
	{
		if($charlist == '') return trim($str);
		return $this->ltrim($this->rtrim($str));
	} 

    public function escapeJSON($string)
    {
        // Escape these characters with a backslash:
        // " \ / \n \r \t \b \f
        $search  = array('\\', "\n", "\t", "\r", "\b", "\f", '"');
        $replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"');
        $string  = str_replace($search, $replace, $string);

        // Escape certain ASCII characters:
        // 0x08 => \b
        // 0x0c => \f
        return str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string);
    }	
    
    public function GetTraslit($str)
    {
        $str = $this->ToLower($str);
        $iso = array("і" => "i" , "є" => "ye" , "ѓ" => "g" , "а" => "a" , "б" => "b" , "в" => "v" , "г" => "g" , "д" => "d" , "е" => "e" , "ё" => "yo" , "ж" => "zh" , "з" => "z" , "и" => "i" , "й" => "j" , "к" => "k" , "л" => "l" , "м" => "m" , "н" => "n" , "о" => "o" , "п" => "p" , "р" => "r" , "с" => "s" , "т" => "t" , "у" => "u" , "ф" => "f" , "х" => "x" , "ц" => "c" , "ч" => "ch" , "ш" => "sh" , "щ" => "shh" , "ъ" => "" , "ы" => "y" , "ь" => "" , "э" => "e" , "ю" => "yu" , "я" => "ya" , " " => "_");
        $str = strtr($str, $iso);
        return preg_replace('|[^a-z0-9-_]|i', '', $str);    	
    }
    
    public function EscapeAbsoluteUrls($text)
    {
    	$paths = array();
    	
    	if (_HOST_NAME) $paths[] = '(?:(?:http|https):)?//(?:www\.)?' .preg_quote(_HOST_NAME._ROOT_URL). '[/]?';
    	$paths[] = preg_quote(_ROOT_URL);

    	$text = preg_replace('#((?:src|href)\s*=\s*[\'"]?\s*)(' .implode('|', $paths). ')#ui', '$1{ROOT_URL}', $text);
    	return $text;
    }
    
    public function SetAbsoluteUrls($text, $setFullUrl = false)
    {
    	$url = $setFullUrl ? 'http://' ._HOST_NAME._ROOT_URL : _ROOT_URL;
    	
    	$replace[] = '{ROOT_URL}';
    	$replace[] = urlencode($replace[0]);
    	
    	return str_replace($replace, $url, $text);
    }    
}